require "yaml"
class OptimizedSearchController < ApplicationController
  include TestInputGenerator
  include EncodeToSQL
                
  before_filter :split_keywords_by_space,
                :except => [:index,:sentences_by_word_sequence,:list_by_word_sequence,:around_sentences]
  before_filter :analyze_json_keywords,
                :only => [:sentences_by_word_sequence,:list_by_word_sequence]
  before_filter :prepare_variables,:except => :index
  before_filter :check_options,
                :only => [:list_by_strings,:list_by_words]
  before_filter :check_sequence_options, :only => :list_by_word_sequence
  before_filter :make_from_sql,
                :only => [:sentences_by_words,:list_by_words]
  before_filter :make_sequence_from_sql,
                :only => [:sentences_by_word_sequence,:list_by_word_sequence]
  before_filter :count_strings,
                :load_sentences_by_strings,
                :only => :sentences_by_strings
  before_filter :count_uniq_strings,
                :load_list_by_strings,
                :only => :list_by_strings
  before_filter :count_words,
                :load_sentences_by_words,
                :only => :sentences_by_words
  before_filter :count_uniq_words,
                :load_list_by_words,
                :only => :list_by_words
  before_filter :count_word_sequence,
                :load_sentences_by_word_sequence,
                :only => :sentences_by_word_sequence
  before_filter :count_uniq_word_sequence,
                :load_list_by_word_sequence,
                :only => :list_by_word_sequence

  # インターフェースを作る当面の間は検索トリガー
  def index
    
  end

  # 文字列を含んだ文章を検索する
  def sentences_by_strings
    @sentences.map!{|data| data.to_setence_by_strings}
    render_json({"total" => @pages.item_count,"sentences" => @sentences})
  end
  
  # 文字列を含んだ単語リストを作成する
  # オプション：centerだけのuniqな単語リストを作れる
  def list_by_strings
    @list.map!{|data| data.attributes }
    render_json({"total" => @pages.item_count,"list" => @list})
  end
  
  # 単語を含んだ文章を検索する
  def sentences_by_words
    @sentences.map!{|data| data.to_sentence_by_words }
    render_json({"total" => @pages.item_count,"sentences" => @sentences})
  end
  
  # 任意の単語リストを作成する
  def list_by_words
    @list.map!{|data| data.attributes }
    render_json({"total" => @pages.item_count,"list" => @list})
  end
  
  # 単語列を含んだ文章を検索する
  def sentences_by_word_sequence
    @sentences.map!{|data| data.to_sentence_by_word_sequence }
    render_json({"total" => @pages.item_count,"sentences" => @sentences})
  end
  
  # 任意の単語列リストを作成する
  def list_by_word_sequence
    @list.map!{|data| encodeAttributes(data.attributes) }
    render_json({"total" => @pages.item_count,"list" => @list})
  end
    
  # 任意のsentence_idの周囲の文を取得する TODO これじゃブラウザ以外のアクセスに対応できないJSONで出力する方法に切り替えよう
  def around_sentences
    @size = params[:size].to_i
    @id = params[:id].to_i
    @start = @id - (@size / 2)
    @sentences = Corpus.find(:all,
      :conditions => ['id >= ?',@start],
      :limit => @size,
      :order => :id)
    xml = Builder::XmlMarkup.new(:indent=>2)
    render :xml => xml.body {
      context = @sentences.partition{|sentence| sentence.id < @id}
      text = ""
      ((context.first.size / 5) + 1).times do |i|
        xml.p(context[0][i * 5,5].map{|sentence|sentence.text}.join(""))
      end      
      xml.p{xml.b{xml.font(context[1][0].text,{"color" => "#ff4500"})}}
      after = context[1][1..-1]
      ((after.size / 5) + 1).times do |i|
        xml.p(after[i * 5,5].map{|sentence|sentence.text}.join(""))
      end
    }
  end
  
  private  
  def encodeAttributes(attr)
    @tag_list = YAML.load_file("config/split_tags.yml").invert
    @key_regexp = Regexp.new(@tag_list.keys.map{|key| "(#{Regexp.escape(key)})"}.join('|'))
    data = {'count' => attr['count']}
    attr.each{|key,value|
      next if key == 'count'
      data[key] = encodeToTypeName(value)
    }
    return data
  end
  
  def encodeToTypeName(data)
    obj = data.strip.split(@key_regexp)[1..-1].map{|e|
      if @tag_list.has_key?(e)
        @tag_list[e] 
      else
        e
      end
    }
    return Hash[*obj]
  end
  
  def split_keywords_by_space
    separator = params[:separator].blank? ? '\s|　' : params[:separator]
    @keywords = params[:keywords].strip.split(Regexp.new(separator)).select{|item| !item.blank? }.map{|word| Regexp.escape(word)}
  end

  def analyze_json_keywords
    @inputs = JsonParser.new.parse(params[:keywords])
    data = @inputs["keywords"]
    data.map! do |word|
      r = word.map{|key,el|
        m = el.strip.split(Regexp.new('\s|　')).select{|item| !item.blank? }.first
        [key,Regexp.escape(m)]
      }
      Hash[*(r.flatten)]
    end
    @inputs["keywords"] = data
    locations = @inputs["locations"]
    locations.map! do |location|
      data = "<"
      unless location.blank?
        m = location.match(/(\d+)(?:\D+(\d+))?/).captures
        loc = m.compact.join(",")
        data = loc unless loc.blank?
      end
      data
    end
    @inputs["locations"] = locations
  end
  
  def prepare_variables
    start = (params[:start] || 1).to_i
    @size = (params[:limit] || 20).to_i 
    @sort_col = (params[:sort] || 'center')
    @sort_dir = (params[:dir] || 'ASC')
    @per_page = ((start / @size).to_i) + 1
  end
  
  def check_options
    @center_uniq = params[:center_uniq] == "check"
    @group = @center_uniq ? "center" : "left_side,center,right_side"
    true # これ，いらないかも
  end
  
  def check_sequence_options
    @center_uniq = params[:center_uniq] == "check"
    @group = @center_uniq ? "center" : make_sequence_group(@inputs["keywords"].size)
    true # これ，いらないかも    
  end
  
  def make_from_sql
    targets = @keywords.nil? ? @inputs["keywords"] : @keywords.map{|keyword| {"surface" => keyword} } 
    from = make_a_short_list_of_corpora(targets)
    @from_sql = from.nil?  ? "corpora" : "(#{from}) as results"
  end
  
  def make_sequence_from_sql
    targets = @keywords.nil? ? @inputs["keywords"] : @keywords.map{|keyword| {"surface" => keyword} } 
    from = make_a_short_sequence_list_of_corpora(targets)
    @from_sql = from.nil?  ? "corpora" : "(#{from}) as results"
  end
  
  def count_strings
    @target = params[:target].blank? ? 'text' : params[:target] ;
    total = params[:total].blank? ? Corpus.count(:conditions => ["#{@target} ~ ?",@keywords.join('|')]) : params[:total].to_i
    @pages = Paginator.new(self,total,@size, @per_page)
  end

  def load_sentences_by_strings
    @sentences = Corpus.find(:all,
      :select => "id, #{strings_select_sql(@keywords)},text,sequence,json",
      :conditions => ["#{@target} ~ ?",@keywords.join('|')],
      :limit => @pages.items_per_page,
      :offset=> @pages.current.offset, 
      :order => @sort_col+' '+@sort_dir)
  end

  def count_uniq_strings
    from_conditions = 
      "SELECT #{strings_select_sql(@keywords,@center_uniq)} FROM corpora WHERE (text ~ '#{@keywords.join('|')}') GROUP BY #{@group}"
    total = params[:total].blank? ? Corpus.count_by_sql("SELECT count(*) AS count_all FROM (#{from_conditions}) AS target") : params[:total].to_i
    @pages = Paginator.new(self,total,@size, @per_page)
  end

  def load_list_by_strings
    order = Array.new
    order << 'center ASC' if !@center_uniq && params[:sort] != 'center'
    order << @sort_col+' '+@sort_dir unless params[:sort].blank?
    order << 'count DESC' unless params[:sort] == 'count'
    @list = Corpus.find(:all,
      :select => "#{strings_select_sql(@keywords,@center_uniq)},count(*)",
      :conditions => ["text ~ ?",@keywords.join('|')],
      :group => @group,
      :limit => @pages.items_per_page,
      :offset=> @pages.current.offset, 
      :order => order.join(','))
  end

  def count_words # 一応良さそうだけどまだ怪しい
    total = params[:total].blank? ? Corpus.count_by_sql("SELECT COUNT(*) FROM #{@from_sql}") : params[:total].to_i
    @pages = Paginator.new(self,total,@size, @per_page)
  end

  def load_sentences_by_words
    @sentences = Corpus.find(:all,
      :select => "id, #{word_select_sql(@keywords)},text,sequence,json",
      :from => @from_sql,
      :limit => @pages.items_per_page,
      :offset=> @pages.current.offset, 
      :order => @sort_col+' '+@sort_dir)
  end

  def count_uniq_words# count_uniq_stringsとまとめられるけど，メンテナンスが逆にし辛くなるかも
    from_conditions = 
      "SELECT #{word_select_sql(@keywords,@center_uniq)} FROM #{@from_sql} GROUP BY #{@group}"
    total = params[:total].blank? ? Corpus.count_by_sql("SELECT count(*) AS count_all FROM (#{from_conditions}) AS target") : params[:total].to_i
    @pages = Paginator.new(self,total,@size, @per_page)
  end

  def load_list_by_words
    order = Array.new
    order << 'center ASC' if !@center_uniq && params[:sort] != 'center'
    order << @sort_col+' '+@sort_dir unless params[:sort].blank?
    order << 'count DESC' unless params[:sort] == 'count'
    @list = Corpus.find(:all,
      :select => "#{word_select_sql(@keywords,@center_uniq)},count(*)",
      :from => @from_sql,
      :group => @group,
      :limit => @pages.items_per_page,
      :offset=> @pages.current.offset, 
      :order => order.join(','))
  end

  def count_word_sequence
    total = params[:total].blank? ? Corpus.count_by_sql("SELECT COUNT(*) FROM #{@from_sql} WHERE data ~ '#{to_word_sequence_regexp(@inputs)}'") : params[:total].to_i
    @pages = Paginator.new(self,total,@size, @per_page)
  end
  
  def load_sentences_by_word_sequence
    @sentences = Corpus.find(:all,
      :select => "id, #{word_sequence_select_sql(@inputs)},text,sequence,data,json",
      :from => @from_sql,
      :conditions => ["data ~ ?",to_word_sequence_regexp(@inputs)],
      :limit => @pages.items_per_page,
      :offset=> @pages.current.offset, 
      :order => @sort_col+' '+@sort_dir)
  end

  def count_uniq_word_sequence 
    from_conditions = 
      "SELECT #{uniq_word_sequence_select_sql(@inputs,@center_uniq)} FROM #{@from_sql} WHERE (data ~ '#{to_word_sequence_regexp(@inputs)}') GROUP BY #{@group}"
    total = params[:total].blank? ? Corpus.count_by_sql("SELECT count(*) AS count_all FROM (#{from_conditions}) AS target") : params[:total].to_i
    @pages = Paginator.new(self,total,@size, @per_page)
  end
  
  def load_list_by_word_sequence
    order = Array.new
    order << @sort_col+' '+@sort_dir unless params[:sort].blank?
    order << 'count DESC' unless params[:sort] == 'count'
    @list = Corpus.find(:all,
      :select => "#{uniq_word_sequence_select_sql(@inputs,@center_uniq)},count(*)",
      :from => @from_sql,
      :conditions => ["data ~ ?",to_word_sequence_regexp(@inputs)],
      :group => @group,
      :limit => @pages.items_per_page,
      :offset=> @pages.current.offset, 
      :order => order.join(','))
   end

end
